﻿using System;
using Loki.Utils;

namespace Loki.Events
{
    internal class WeakEventBridge<TEventInterface, TEventArgs>
    {
        private TEventInterface _Source;

        private ConcurrentCollection<IWeakEventCallback<TEventArgs>> _Callbacks;

        private Action<TEventInterface, WeakEventBridge<TEventInterface, TEventArgs>> _UnsubscribeCallback;

        /// <summary>
        /// Gets a value indicating whether the event source has active listeners.
        /// </summary>
        /// <value>
        ///     Is <c>true</c> if the event source has active listeners; otherwise, <c>false</c>.
        /// </value>
        public bool HasActiveListeners
        {
            get { return !_Callbacks.IsEmpty; }
        }

        public WeakEventBridge(
            TEventInterface P_Source,
            Action<TEventInterface, WeakEventBridge<TEventInterface, TEventArgs>> P_EventMapper,
            Action<TEventInterface, WeakEventBridge<TEventInterface, TEventArgs>> P_Unsubscribe)
        {
            _Source = P_Source;
            _Callbacks = new ConcurrentCollection<IWeakEventCallback<TEventArgs>>();
            P_EventMapper(_Source, this);

            _UnsubscribeCallback = P_Unsubscribe;
        }

        /// <summary>
        /// Adds the listener.
        /// </summary>
        /// <typeparam name="TListener">The type of the listener.</typeparam>
        /// <param name="P_Listener">The listener.</param>
        /// <param name="P_StateChangedCallback">The state changed callback.</param>
        public void AddListener<TListener>(
            TListener P_Listener,
            Action<TListener, object, TEventArgs> P_StateChangedCallback)
        {
            var L_Callback = new WeakEventCallback<TListener, TEventArgs>(P_Listener, P_StateChangedCallback);
            _Callbacks.Add(L_Callback);
        }

        /// <summary>
        /// Removes the listener.
        /// </summary>
        /// <param name="P_Listener">The listener.</param>
        public void RemoveListener(object P_Listener)
        {
            foreach (var L_Node in _Callbacks)
            {
                IWeakEventCallback<TEventArgs> L_Callback = L_Node.Value;
                object L_ListenerForCallback = L_Callback.Listener;

                if (L_ListenerForCallback == null)
                {
                    _Callbacks.Remove(L_Node);
                }
                else if (L_ListenerForCallback == P_Listener)
                {
                    _Callbacks.Remove(L_Node);
                    break;
                }
            }

            UnsubscribeIfNoMoreListeners();
        }

        public void OnEvent(object P_Sender, TEventArgs e)
        {
            foreach (var L_Node in _Callbacks)
            {
                IWeakEventCallback<TEventArgs> L_Callback = L_Node.Value;

                if (!L_Callback.Invoke(P_Sender, e))
                {
                    _Callbacks.Remove(L_Node);
                }
            }
        }

        private void UnsubscribeIfNoMoreListeners()
        {
            if (!this.HasActiveListeners)
            {
                _UnsubscribeCallback(_Source, this);
            }
        }
    }
}
